/* ---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *-------------------------------------------------------------------------------------------- */

import { OperatingSystem } from "./platform";

/**
 * Virtual Key Codes, the value does not hold any inherent meaning.
 * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
 * But these are "more general", as they should work across browsers & OS`s.
 */
export const enum KeyCode {
  /**
   * Placed first to cover the 0 value of the enum.
   */
  Unknown = 0,

  Backspace = 1,
  Tab = 2,
  Enter = 3,
  Shift = 4,
  Ctrl = 5,
  Alt = 6,
  PauseBreak = 7,
  CapsLock = 8,
  Escape = 9,
  Space = 10,
  PageUp = 11,
  PageDown = 12,
  End = 13,
  Home = 14,
  LeftArrow = 15,
  UpArrow = 16,
  RightArrow = 17,
  DownArrow = 18,
  Insert = 19,
  Delete = 20,

  KEY_0 = 21,
  KEY_1 = 22,
  KEY_2 = 23,
  KEY_3 = 24,
  KEY_4 = 25,
  KEY_5 = 26,
  KEY_6 = 27,
  KEY_7 = 28,
  KEY_8 = 29,
  KEY_9 = 30,

  KEY_A = 31,
  KEY_B = 32,
  KEY_C = 33,
  KEY_D = 34,
  KEY_E = 35,
  KEY_F = 36,
  KEY_G = 37,
  KEY_H = 38,
  KEY_I = 39,
  KEY_J = 40,
  KEY_K = 41,
  KEY_L = 42,
  KEY_M = 43,
  KEY_N = 44,
  KEY_O = 45,
  KEY_P = 46,
  KEY_Q = 47,
  KEY_R = 48,
  KEY_S = 49,
  KEY_T = 50,
  KEY_U = 51,
  KEY_V = 52,
  KEY_W = 53,
  KEY_X = 54,
  KEY_Y = 55,
  KEY_Z = 56,

  Meta = 57,
  ContextMenu = 58,

  F1 = 59,
  F2 = 60,
  F3 = 61,
  F4 = 62,
  F5 = 63,
  F6 = 64,
  F7 = 65,
  F8 = 66,
  F9 = 67,
  F10 = 68,
  F11 = 69,
  F12 = 70,
  F13 = 71,
  F14 = 72,
  F15 = 73,
  F16 = 74,
  F17 = 75,
  F18 = 76,
  F19 = 77,

  NumLock = 78,
  ScrollLock = 79,

  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the ';:' key
   */
  US_SEMICOLON = 80,
  /**
   * For any country/region, the '+' key
   * For the US standard keyboard, the '=+' key
   */
  US_EQUAL = 81,
  /**
   * For any country/region, the ',' key
   * For the US standard keyboard, the ',<' key
   */
  US_COMMA = 82,
  /**
   * For any country/region, the '-' key
   * For the US standard keyboard, the '-_' key
   */
  US_MINUS = 83,
  /**
   * For any country/region, the '.' key
   * For the US standard keyboard, the '.>' key
   */
  US_DOT = 84,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the '/?' key
   */
  US_SLASH = 85,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the '`~' key
   */
  US_BACKTICK = 86,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the '[{' key
   */
  US_OPEN_SQUARE_BRACKET = 87,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the '\|' key
   */
  US_BACKSLASH = 88,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the ']}' key
   */
  US_CLOSE_SQUARE_BRACKET = 89,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   * For the US standard keyboard, the ''"' key
   */
  US_QUOTE = 90,
  /**
   * Used for miscellaneous characters; it can vary by keyboard.
   */
  OEM_8 = 91,
  /**
   * Either the angle bracket key or the backslash key on the RT 102-key keyboard.
   */
  OEM_102 = 92,

  NUMPAD_0 = 93, // VK_NUMPAD0, 0x60, Numeric keypad 0 key
  NUMPAD_1 = 94, // VK_NUMPAD1, 0x61, Numeric keypad 1 key
  NUMPAD_2 = 95, // VK_NUMPAD2, 0x62, Numeric keypad 2 key
  NUMPAD_3 = 96, // VK_NUMPAD3, 0x63, Numeric keypad 3 key
  NUMPAD_4 = 97, // VK_NUMPAD4, 0x64, Numeric keypad 4 key
  NUMPAD_5 = 98, // VK_NUMPAD5, 0x65, Numeric keypad 5 key
  NUMPAD_6 = 99, // VK_NUMPAD6, 0x66, Numeric keypad 6 key
  NUMPAD_7 = 100, // VK_NUMPAD7, 0x67, Numeric keypad 7 key
  NUMPAD_8 = 101, // VK_NUMPAD8, 0x68, Numeric keypad 8 key
  NUMPAD_9 = 102, // VK_NUMPAD9, 0x69, Numeric keypad 9 key

  NUMPAD_MULTIPLY = 103, // VK_MULTIPLY, 0x6A, Multiply key
  NUMPAD_ADD = 104, // VK_ADD, 0x6B, Add key
  NUMPAD_SEPARATOR = 105, // VK_SEPARATOR, 0x6C, Separator key
  NUMPAD_SUBTRACT = 106, // VK_SUBTRACT, 0x6D, Subtract key
  NUMPAD_DECIMAL = 107, // VK_DECIMAL, 0x6E, Decimal key
  NUMPAD_DIVIDE = 108, // VK_DIVIDE, 0x6F,

  /**
   * Cover all key codes when IME is processing input.
   */
  KEY_IN_COMPOSITION = 109,

  ABNT_C1 = 110, // Brazilian (ABNT) Keyboard
  ABNT_C2 = 111, // Brazilian (ABNT) Keyboard

  /**
   * Placed last to cover the length of the enum.
   * Please do not depend on this value!
   */
  MAX_VALUE,
}

/**
 * keyboardEvent.code
 */
export const enum ScanCode {
  DependsOnKbLayout = -1,
  None,
  Hyper,
  Super,
  Fn,
  FnLock,
  Suspend,
  Resume,
  Turbo,
  Sleep,
  WakeUp,
  KeyA,
  KeyB,
  KeyC,
  KeyD,
  KeyE,
  KeyF,
  KeyG,
  KeyH,
  KeyI,
  KeyJ,
  KeyK,
  KeyL,
  KeyM,
  KeyN,
  KeyO,
  KeyP,
  KeyQ,
  KeyR,
  KeyS,
  KeyT,
  KeyU,
  KeyV,
  KeyW,
  KeyX,
  KeyY,
  KeyZ,
  Digit1,
  Digit2,
  Digit3,
  Digit4,
  Digit5,
  Digit6,
  Digit7,
  Digit8,
  Digit9,
  Digit0,
  Enter,
  Escape,
  Backspace,
  Tab,
  Space,
  Minus,
  Equal,
  BracketLeft,
  BracketRight,
  Backslash,
  IntlHash,
  Semicolon,
  Quote,
  Backquote,
  Comma,
  Period,
  Slash,
  CapsLock,
  F1,
  F2,
  F3,
  F4,
  F5,
  F6,
  F7,
  F8,
  F9,
  F10,
  F11,
  F12,
  PrintScreen,
  ScrollLock,
  Pause,
  Insert,
  Home,
  PageUp,
  Delete,
  End,
  PageDown,
  ArrowRight,
  ArrowLeft,
  ArrowDown,
  ArrowUp,
  NumLock,
  NumpadDivide,
  NumpadMultiply,
  NumpadSubtract,
  NumpadAdd,
  NumpadEnter,
  Numpad1,
  Numpad2,
  Numpad3,
  Numpad4,
  Numpad5,
  Numpad6,
  Numpad7,
  Numpad8,
  Numpad9,
  Numpad0,
  NumpadDecimal,
  IntlBackslash,
  ContextMenu,
  Power,
  NumpadEqual,
  F13,
  F14,
  F15,
  F16,
  F17,
  F18,
  F19,
  F20,
  F21,
  F22,
  F23,
  F24,
  Open,
  Help,
  Select,
  Again,
  Undo,
  Cut,
  Copy,
  Paste,
  Find,
  AudioVolumeMute,
  AudioVolumeUp,
  AudioVolumeDown,
  NumpadComma,
  IntlRo,
  KanaMode,
  IntlYen,
  Convert,
  NonConvert,
  Lang1,
  Lang2,
  Lang3,
  Lang4,
  Lang5,
  Abort,
  Props,
  NumpadParenLeft,
  NumpadParenRight,
  NumpadBackspace,
  NumpadMemoryStore,
  NumpadMemoryRecall,
  NumpadMemoryClear,
  NumpadMemoryAdd,
  NumpadMemorySubtract,
  NumpadClear,
  NumpadClearEntry,
  ControlLeft,
  ShiftLeft,
  AltLeft,
  MetaLeft,
  ControlRight,
  ShiftRight,
  AltRight,
  MetaRight,
  BrightnessUp,
  BrightnessDown,
  MediaPlay,
  MediaRecord,
  MediaFastForward,
  MediaRewind,
  MediaTrackNext,
  MediaTrackPrevious,
  MediaStop,
  Eject,
  MediaPlayPause,
  MediaSelect,
  LaunchMail,
  LaunchApp2,
  LaunchApp1,
  SelectTask,
  LaunchScreenSaver,
  BrowserSearch,
  BrowserHome,
  BrowserBack,
  BrowserForward,
  BrowserStop,
  BrowserRefresh,
  BrowserFavorites,
  ZoomToggle,
  MailReply,
  MailForward,
  MailSend,

  MAX_VALUE,
}

class KeyCodeStrMap {
  private _keyCodeToStr: string[];
  private _strToKeyCode: { [str: string]: KeyCode };

  constructor() {
    this._keyCodeToStr = [];
    this._strToKeyCode = Object.create(null);
  }

  define(keyCode: KeyCode, str: string): void {
    this._keyCodeToStr[keyCode] = str;
    this._strToKeyCode[str.toLowerCase()] = keyCode;
  }

  keyCodeToStr(keyCode: KeyCode): string {
    return this._keyCodeToStr[keyCode];
  }

  strToKeyCode(str: string): KeyCode {
    return this._strToKeyCode[str.toLowerCase()] || KeyCode.Unknown;
  }
}

const scanCodeIntToStr: string[] = [];
const scanCodeStrToInt: { [code: string]: number } = Object.create(null);
const scanCodeLowerCaseStrToInt: { [code: string]: number } = Object.create(null);

export const ScanCodeUtils = {
  lowerCaseToEnum: (scanCode: string) => scanCodeLowerCaseStrToInt[scanCode] || ScanCode.None,
  toEnum: (scanCode: string) => scanCodeStrToInt[scanCode] || ScanCode.None,
  toString: (scanCode: ScanCode) => scanCodeIntToStr[scanCode] || "None",
};

const uiMap = new KeyCodeStrMap();
const userSettingsUSMap = new KeyCodeStrMap();
const userSettingsGeneralMap = new KeyCodeStrMap();

(function () {
  function define(
    keyCode: KeyCode,
    uiLabel: string,
    usUserSettingsLabel: string = uiLabel,
    generalUserSettingsLabel: string = usUserSettingsLabel,
  ): void {
    uiMap.define(keyCode, uiLabel);
    userSettingsUSMap.define(keyCode, usUserSettingsLabel);
    userSettingsGeneralMap.define(keyCode, generalUserSettingsLabel);
  }

  define(KeyCode.Unknown, "unknown");

  define(KeyCode.Backspace, "Backspace");
  define(KeyCode.Tab, "Tab");
  define(KeyCode.Enter, "Enter");
  define(KeyCode.Shift, "Shift");
  define(KeyCode.Ctrl, "Ctrl");
  define(KeyCode.Alt, "Alt");
  define(KeyCode.PauseBreak, "PauseBreak");
  define(KeyCode.CapsLock, "CapsLock");
  define(KeyCode.Escape, "Escape");
  define(KeyCode.Space, "Space");
  define(KeyCode.PageUp, "PageUp");
  define(KeyCode.PageDown, "PageDown");
  define(KeyCode.End, "End");
  define(KeyCode.Home, "Home");

  define(KeyCode.LeftArrow, "LeftArrow", "Left");
  define(KeyCode.UpArrow, "UpArrow", "Up");
  define(KeyCode.RightArrow, "RightArrow", "Right");
  define(KeyCode.DownArrow, "DownArrow", "Down");
  define(KeyCode.Insert, "Insert");
  define(KeyCode.Delete, "Delete");

  define(KeyCode.KEY_0, "0");
  define(KeyCode.KEY_1, "1");
  define(KeyCode.KEY_2, "2");
  define(KeyCode.KEY_3, "3");
  define(KeyCode.KEY_4, "4");
  define(KeyCode.KEY_5, "5");
  define(KeyCode.KEY_6, "6");
  define(KeyCode.KEY_7, "7");
  define(KeyCode.KEY_8, "8");
  define(KeyCode.KEY_9, "9");

  define(KeyCode.KEY_A, "A");
  define(KeyCode.KEY_B, "B");
  define(KeyCode.KEY_C, "C");
  define(KeyCode.KEY_D, "D");
  define(KeyCode.KEY_E, "E");
  define(KeyCode.KEY_F, "F");
  define(KeyCode.KEY_G, "G");
  define(KeyCode.KEY_H, "H");
  define(KeyCode.KEY_I, "I");
  define(KeyCode.KEY_J, "J");
  define(KeyCode.KEY_K, "K");
  define(KeyCode.KEY_L, "L");
  define(KeyCode.KEY_M, "M");
  define(KeyCode.KEY_N, "N");
  define(KeyCode.KEY_O, "O");
  define(KeyCode.KEY_P, "P");
  define(KeyCode.KEY_Q, "Q");
  define(KeyCode.KEY_R, "R");
  define(KeyCode.KEY_S, "S");
  define(KeyCode.KEY_T, "T");
  define(KeyCode.KEY_U, "U");
  define(KeyCode.KEY_V, "V");
  define(KeyCode.KEY_W, "W");
  define(KeyCode.KEY_X, "X");
  define(KeyCode.KEY_Y, "Y");
  define(KeyCode.KEY_Z, "Z");

  define(KeyCode.Meta, "Meta");
  define(KeyCode.ContextMenu, "ContextMenu");

  define(KeyCode.F1, "F1");
  define(KeyCode.F2, "F2");
  define(KeyCode.F3, "F3");
  define(KeyCode.F4, "F4");
  define(KeyCode.F5, "F5");
  define(KeyCode.F6, "F6");
  define(KeyCode.F7, "F7");
  define(KeyCode.F8, "F8");
  define(KeyCode.F9, "F9");
  define(KeyCode.F10, "F10");
  define(KeyCode.F11, "F11");
  define(KeyCode.F12, "F12");
  define(KeyCode.F13, "F13");
  define(KeyCode.F14, "F14");
  define(KeyCode.F15, "F15");
  define(KeyCode.F16, "F16");
  define(KeyCode.F17, "F17");
  define(KeyCode.F18, "F18");
  define(KeyCode.F19, "F19");

  define(KeyCode.NumLock, "NumLock");
  define(KeyCode.ScrollLock, "ScrollLock");

  define(KeyCode.US_SEMICOLON, ";", ";", "OEM_1");
  define(KeyCode.US_EQUAL, "=", "=", "OEM_PLUS");
  define(KeyCode.US_COMMA, ",", ",", "OEM_COMMA");
  define(KeyCode.US_MINUS, "-", "-", "OEM_MINUS");
  define(KeyCode.US_DOT, ".", ".", "OEM_PERIOD");
  define(KeyCode.US_SLASH, "/", "/", "OEM_2");
  define(KeyCode.US_BACKTICK, "`", "`", "OEM_3");
  define(KeyCode.ABNT_C1, "ABNT_C1");
  define(KeyCode.ABNT_C2, "ABNT_C2");
  define(KeyCode.US_OPEN_SQUARE_BRACKET, "[", "[", "OEM_4");
  define(KeyCode.US_BACKSLASH, "\\", "\\", "OEM_5");
  define(KeyCode.US_CLOSE_SQUARE_BRACKET, "]", "]", "OEM_6");
  define(KeyCode.US_QUOTE, "'", "'", "OEM_7");
  define(KeyCode.OEM_8, "OEM_8");
  define(KeyCode.OEM_102, "OEM_102");

  define(KeyCode.NUMPAD_0, "NumPad0");
  define(KeyCode.NUMPAD_1, "NumPad1");
  define(KeyCode.NUMPAD_2, "NumPad2");
  define(KeyCode.NUMPAD_3, "NumPad3");
  define(KeyCode.NUMPAD_4, "NumPad4");
  define(KeyCode.NUMPAD_5, "NumPad5");
  define(KeyCode.NUMPAD_6, "NumPad6");
  define(KeyCode.NUMPAD_7, "NumPad7");
  define(KeyCode.NUMPAD_8, "NumPad8");
  define(KeyCode.NUMPAD_9, "NumPad9");

  define(KeyCode.NUMPAD_MULTIPLY, "NumPad_Multiply");
  define(KeyCode.NUMPAD_ADD, "NumPad_Add");
  define(KeyCode.NUMPAD_SEPARATOR, "NumPad_Separator");
  define(KeyCode.NUMPAD_SUBTRACT, "NumPad_Subtract");
  define(KeyCode.NUMPAD_DECIMAL, "NumPad_Decimal");
  define(KeyCode.NUMPAD_DIVIDE, "NumPad_Divide");
})();

export namespace KeyCodeUtils {
  export function toString(keyCode: KeyCode): string {
    return uiMap.keyCodeToStr(keyCode);
  }
  export function fromString(key: string): KeyCode {
    return uiMap.strToKeyCode(key);
  }

  export function toUserSettingsUS(keyCode: KeyCode): string {
    return userSettingsUSMap.keyCodeToStr(keyCode);
  }
  export function toUserSettingsGeneral(keyCode: KeyCode): string {
    return userSettingsGeneralMap.keyCodeToStr(keyCode);
  }
  export function fromUserSettings(key: string): KeyCode {
    return userSettingsUSMap.strToKeyCode(key) || userSettingsGeneralMap.strToKeyCode(key);
  }
}

/**
 * Binary encoding strategy:
 * ```
 *    1111 11
 *    5432 1098 7654 3210
 *    ---- CSAW KKKK KKKK
 *  C = bit 11 = ctrlCmd flag
 *  S = bit 10 = shift flag
 *  A = bit 9 = alt flag
 *  W = bit 8 = winCtrl flag
 *  K = bits 0-7 = key code
 * ```
 */
const enum BinaryKeybindingsMask {
  CtrlCmd = (1 << 11) >>> 0,
  Shift = (1 << 10) >>> 0,
  Alt = (1 << 9) >>> 0,
  WinCtrl = (1 << 8) >>> 0,
  KeyCode = 0x000000ff,
}

export const enum KeyMod {
  CtrlCmd = (1 << 11) >>> 0,
  Shift = (1 << 10) >>> 0,
  Alt = (1 << 9) >>> 0,
  WinCtrl = (1 << 8) >>> 0,
}

export function KeyChord(firstPart: number, secondPart: number): number {
  const chordPart = ((secondPart & 0x0000ffff) << 16) >>> 0;
  return (firstPart | chordPart) >>> 0;
}

export const enum KeybindingType {
  Simple = 1,
  Chord = 2,
}

export class SimpleKeybinding {
  public readonly type = KeybindingType.Simple;

  public readonly ctrlKey: boolean;
  public readonly shiftKey: boolean;
  public readonly altKey: boolean;
  public readonly metaKey: boolean;
  public readonly keyCode: KeyCode;

  constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, keyCode: KeyCode) {
    this.ctrlKey = ctrlKey;
    this.shiftKey = shiftKey;
    this.altKey = altKey;
    this.metaKey = metaKey;
    this.keyCode = keyCode;
  }

  // eslint-disable-next-line no-use-before-define
  public equals(other: Keybinding): boolean {
    if (other.type !== KeybindingType.Simple) {
      return false;
    }
    return (
      this.ctrlKey === other.ctrlKey &&
      this.shiftKey === other.shiftKey &&
      this.altKey === other.altKey &&
      this.metaKey === other.metaKey &&
      this.keyCode === other.keyCode
    );
  }

  public getHashCode(): string {
    const ctrl = this.ctrlKey ? "1" : "0";
    const shift = this.shiftKey ? "1" : "0";
    const alt = this.altKey ? "1" : "0";
    const meta = this.metaKey ? "1" : "0";
    return `${ctrl}${shift}${alt}${meta}${this.keyCode}`;
  }

  public isModifierKey(): boolean {
    return (
      this.keyCode === KeyCode.Unknown ||
      this.keyCode === KeyCode.Ctrl ||
      this.keyCode === KeyCode.Meta ||
      this.keyCode === KeyCode.Alt ||
      this.keyCode === KeyCode.Shift
    );
  }

  /**
   * Does this keybinding refer to the key code of a modifier and it also has the modifier flag?
   */
  public isDuplicateModifierCase(): boolean {
    return (
      (this.ctrlKey && this.keyCode === KeyCode.Ctrl) ||
      (this.shiftKey && this.keyCode === KeyCode.Shift) ||
      (this.altKey && this.keyCode === KeyCode.Alt) ||
      (this.metaKey && this.keyCode === KeyCode.Meta)
    );
  }
}

export class ChordKeybinding {
  public readonly type = KeybindingType.Chord;

  public readonly firstPart: SimpleKeybinding;
  public readonly chordPart: SimpleKeybinding;

  constructor(firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) {
    this.firstPart = firstPart;
    this.chordPart = chordPart;
  }

  public getHashCode(): string {
    return `${this.firstPart.getHashCode()};${this.chordPart.getHashCode()}`;
  }
}

export type Keybinding = SimpleKeybinding | ChordKeybinding;

export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybinding | null {
  if (keybinding === 0) {
    return null;
  }
  const firstPart = (keybinding & 0x0000ffff) >>> 0;
  const chordPart = (keybinding & 0xffff0000) >>> 16;
  if (chordPart !== 0) {
    return new ChordKeybinding(createSimpleKeybinding(firstPart, OS), createSimpleKeybinding(chordPart, OS));
  }
  return createSimpleKeybinding(firstPart, OS);
}

export function createSimpleKeybinding(keybinding: number, OS: OperatingSystem): SimpleKeybinding {
  const ctrlCmd = !!(keybinding & BinaryKeybindingsMask.CtrlCmd);
  const winCtrl = !!(keybinding & BinaryKeybindingsMask.WinCtrl);

  const ctrlKey = OS === OperatingSystem.Macintosh ? winCtrl : ctrlCmd;
  const shiftKey = !!(keybinding & BinaryKeybindingsMask.Shift);
  const altKey = !!(keybinding & BinaryKeybindingsMask.Alt);
  const metaKey = OS === OperatingSystem.Macintosh ? ctrlCmd : winCtrl;
  const keyCode = keybinding & BinaryKeybindingsMask.KeyCode;

  return new SimpleKeybinding(ctrlKey, shiftKey, altKey, metaKey, keyCode);
}

export class ResolvedKeybindingPart {
  readonly ctrlKey: boolean;
  readonly shiftKey: boolean;
  readonly altKey: boolean;
  readonly metaKey: boolean;

  readonly keyLabel: string | null;
  readonly keyAriaLabel: string | null;

  constructor(
    ctrlKey: boolean,
    shiftKey: boolean,
    altKey: boolean,
    metaKey: boolean,
    kbLabel: string | null,
    kbAriaLabel: string | null,
  ) {
    this.ctrlKey = ctrlKey;
    this.shiftKey = shiftKey;
    this.altKey = altKey;
    this.metaKey = metaKey;
    this.keyLabel = kbLabel;
    this.keyAriaLabel = kbAriaLabel;
  }
}

/**
 * A resolved keybinding. Can be a simple keybinding or a chord keybinding.
 */
export abstract class ResolvedKeybinding {
  /**
   * This prints the binding in a format suitable for displaying in the UI.
   */
  public abstract getLabel(): string | null;
  /**
   * This prints the binding in a format suitable for ARIA.
   */
  public abstract getAriaLabel(): string | null;
  /**
   * This prints the binding in a format suitable for electron's accelerators.
   * See https://github.com/electron/electron/blob/master/docs/api/accelerator.md
   */
  public abstract getElectronAccelerator(): string | null;
  /**
   * This prints the binding in a format suitable for user settings.
   */
  public abstract getUserSettingsLabel(): string | null;
  /**
   * Is the user settings label reflecting the label?
   */
  public abstract isWYSIWYG(): boolean;

  /**
   * Is the binding a chord?
   */
  public abstract isChord(): boolean;

  /**
   * Returns the firstPart, chordPart that should be used for dispatching.
   */
  public abstract getDispatchParts(): [string | null, string | null];
  /**
   * Returns the firstPart, chordPart of the keybinding.
   * For simple keybindings, the second element will be null.
   */
  public abstract getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart | null];
}
